/*
 * Copyright (c) 2006-2023, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2018-08-24     yangjie      the first version
 */

/*
 * 程序清单：生产者消费者例子
 *
 * 这个例子中将创建两个线程用于实现生产者消费者问题
 *（1）生产者线程将cnt值每次加1并循环存入array数组的5个成员内；
 *（2）消费者线程将生产者中生产的数值打印出来，并累加求和
 */
#include <aos/aos.h>
#include "aos/cli.h"
#include <debug/dbg.h>

#define THREAD_PRIORITY       6
#define THREAD_STACK_SIZE     2048
#define THREAD_TIMESLICE      5

/* 定义最大5个元素能够被产生 */
#define MAXSEM 5

/* 用于放置生产的整数数组 */
static unsigned int array[MAXSEM];

/* 指向生产者、消费者在array数组中的读写位置 */
static unsigned int set, get;

/* 指向线程控制块的指针 */
static aos_task_t producer_tid = NULL;
static aos_task_t consumer_tid = NULL;

static aos_sem_t sem_lock;
static aos_sem_t sem_empty, sem_full;

/* 生产者线程入口 */
static void producer_thread_entry(void *parameter)
{
    int cnt = 0;

    /* 运行10次 */
    while (cnt < 10)
    {
        /* 获取一个空位 */
        aos_sem_wait(&sem_empty, AOS_WAIT_FOREVER);

        /* 修改array内容，上锁 */
        aos_sem_wait(&sem_lock, AOS_WAIT_FOREVER);
        array[set % MAXSEM] = cnt + 1;
        aos_debug_printf("the producer generates a number: %d\n", array[set % MAXSEM]);
        set++;
        aos_sem_signal(&sem_lock);

        /* 发布一个满位 */
        aos_sem_signal(&sem_full);
        cnt++;

        /* 暂停一段时间 */
        aos_msleep(20);
    }

    aos_debug_printf("the producer exit!\n");
}

/* 消费者线程入口 */
static void consumer_thread_entry(void *parameter)
{
    unsigned int sum = 0;

    while (1)
    {
        /* 获取一个满位 */
        aos_sem_wait(&sem_full, AOS_WAIT_FOREVER);

        /* 临界区，上锁进行操作 */
        aos_sem_wait(&sem_lock, AOS_WAIT_FOREVER);
        sum += array[get % MAXSEM];
        aos_debug_printf("the consumer[%d] get a number: %d\n", (get % MAXSEM), array[get % MAXSEM]);
        get++;
        aos_sem_signal(&sem_lock);

        /* 释放一个空位 */
        aos_sem_signal(&sem_empty);

        /* 生产者生产到10个数目，停止，消费者线程相应停止 */
        if (get == 10) break;

        /* 暂停一小会时间 */
        aos_msleep(50);
    }

    aos_debug_printf("the consumer sum is: %d\n", sum);
    aos_debug_printf("the consumer exit!\n");
}

int yoc_producer_consumer(int argc, char **argv)
{
    set = 0;
    get = 0;

    /* 初始化3个信号量 */

    aos_sem_create(&sem_lock, 1, 0);
    aos_sem_create(&sem_empty, MAXSEM, 0);
    aos_sem_create(&sem_full,  0, 0);

    /* 创建生产者线程 */
    aos_task_create(&producer_tid,
                    "producer",
                    producer_thread_entry, NULL,
                    NULL,
                    THREAD_STACK_SIZE,
                    THREAD_PRIORITY - 1,
                    AOS_TASK_AUTORUN);

    /* 创建消费者线程 */
    aos_task_create(&consumer_tid,
                    "consumer",
                    consumer_thread_entry, NULL,
                    NULL,
                    THREAD_STACK_SIZE,
                    THREAD_PRIORITY + 1,
                    AOS_TASK_AUTORUN);

    return 0;
}

ALIOS_CLI_CMD_REGISTER(yoc_producer_consumer, yoc_producer_consumer,yoc producer consumer);

